package org.dingfugui.triple.protocol.binary.aio;

import org.dingfugui.triple.common.Logger;
import org.dingfugui.triple.common.LoggerFactory;
import org.dingfugui.triple.protocol.AbstractAcceptor;
import org.dingfugui.triple.protocol.RequestHandler;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.TimeUnit;

/**
 * Created by fugui.ding on 2015/4/20.
 */
public class AioAcceptor extends AbstractAcceptor {

    private Logger logger = LoggerFactory.getLogger(getClass());
    private int port;
    private RequestHandler requestHandler;
    private AsynchronousServerSocketChannel serverSocket;
    private AsynchronousChannelGroup group;

    public AioAcceptor(RequestHandler requestHandler) {
        this(DEFAULT_PORT, requestHandler);
    }

    public AioAcceptor(int port, RequestHandler requestHandler) {
        this.port = port;
        this.requestHandler = requestHandler;
    }

    @Override
    public void accept() throws IOException {
        group = AsynchronousChannelGroup.withThreadPool(workThreadPool);
        serverSocket = createListener(group);

        run();
    }

    public void run() {
        serverSocket.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            @Override
            public void completed(AsynchronousSocketChannel result, Object attachment) {
                serverSocket.accept(null, this);
                handleNewConnection(result);
            }

            @Override
            public void failed(Throwable exc, Object attachment) {
                serverSocket.accept(null, this);
            }
        });
        logger.info("服务开启,端口:{}", port);
    }

    private void handleNewConnection(AsynchronousSocketChannel socket) {
        try {
            socket.setOption(StandardSocketOptions.TCP_NODELAY, true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        AioProducerConnection con = new AioProducerConnection(socket, requestHandler);
        AioReader reader = new AioReader(con);
        reader.run();
    }

    private AsynchronousServerSocketChannel createListener(AsynchronousChannelGroup channelGroup) throws IOException {
        final AsynchronousServerSocketChannel listener = openChannel(channelGroup);
        listener.setOption(StandardSocketOptions.SO_REUSEADDR, true);
        listener.bind(new InetSocketAddress(port));
        return listener;
    }

    private AsynchronousServerSocketChannel openChannel(AsynchronousChannelGroup channelGroup) throws IOException {
        return AsynchronousServerSocketChannel.open(channelGroup);
    }

    public void shutdown() throws InterruptedException, IOException {
        group.shutdownNow();
        group.awaitTermination(1, TimeUnit.SECONDS);
    }

    public void setRequestHandler(RequestHandler requestHandler) {
        this.requestHandler = requestHandler;
    }


}
